/*!
\copyright  Copyright (c) 2025 Qualcomm Technologies International, Ltd.
            All Rights Reserved.
            Qualcomm Technologies International, Ltd. Confidential and Proprietary.
\file      
\ingroup    stats_monitor
\brief      Implementation of the stats monitoring task that periodically logs statistics.
*/

#ifdef INCLUDE_STATS_MONITOR

#include "stats_monitor.h"
#include "stats_monitor_utils.h"
#include "context_types.h"
#include "context_framework.h"
#include <logging.h>
#include <panic.h>
#include "audio_stack_channel_event_spec.h"
#include "event_bus_subscribe.h"
#include "vm.h"
#include "system_keys_prim.h"
#include "message.h"

#define STATS_MONITOR_TASK  (&stats_monitor_task_data)

#define STATS_MONITOR_LOG       DEBUG_LOG_ALWAYS

#define AUDIO_CORE_ID_MDSP          0
#define AUDIO_CORE_ID_LLDSP         1
#define AUDIO_CORE_ID_ODSP          2

typedef struct
{ 
    context_streaming_info_t ctx_streaming_info;
    uint8 active_use_cases;
    bool updated;
} stats_monitor_streaming_t;

typedef struct
{
    uint16 mdsp_mcps;
    uint16 lldsp_mcps;
    uint16 odsp_mcps;
    uint16 audio_clock;
    uint8 active_use_cases;
    bool updated;
} stats_monitor_audio_mcps_t;

typedef enum
{
    STATS_MONITOR_INTERNAL_MSG_READ_STATS,
} stats_monitor_internal_msg_ids;

static void statsMonitor_MsgHandler(Task task, MessageId id, Message msg);

static stats_monitor_streaming_t streaming_info = {0};
static stats_monitor_audio_mcps_t audio_mcps = {0};

static uint16 stats_monitor_interval_ms = DEFAULT_STATS_MONITOR_INTERVAL_MS;
static const TaskData stats_monitor_task_data = {statsMonitor_MsgHandler};


static void statsMonitor_UseCasePrepareCallback(audio_stack_use_cases_t use_case)
{
    DEBUG_LOG_VERBOSE("statsMonitor_UseCasePrepareCallback enum:audio_stack_use_cases_t:0x%x", use_case);

    switch(use_case)
    {
        case USE_CASE_STREAMING:
        {
            /* stats applicable to streaming are streaming_info */
            streaming_info.active_use_cases++;
        }
        /* fallthrough */
        default:
        {
            /* MCPS applies to all the DSP use cases */
            audio_mcps.active_use_cases++;
        }
        break;
    }
}

static void statsMonitor_UseCaseStopCallback(audio_stack_use_cases_t use_case)
{
    DEBUG_LOG_VERBOSE("statsMonitor_UseCaseStopCallback enum:audio_stack_use_cases_t:0x%x", use_case);
    switch(use_case)
    {
        case USE_CASE_STREAMING:
        {
            if(streaming_info.active_use_cases)
                streaming_info.active_use_cases--;
        }
        /* fallthrough */
        default:
        {
            if(audio_mcps.active_use_cases)
                audio_mcps.active_use_cases--;
        }
        break;
    }
}

static void statsMonitor_OnEvent(event_bus_channel_t channel, event_bus_event_t event, void *data, uint16 data_size, const event_bus_subscriber_t *subscriber)
{
    if (channel == EVENT_BUS_CHANNEL_AUDIO_STACK)
    {
        switch (event)
        {
            case audio_stack_event_usecase_prepare:
            {
                audio_stack_event_usecase_prepare_t *event_data = data;
                statsMonitor_UseCasePrepareCallback(event_data->common.use_case);
                break;
            }
            case audio_stack_event_usecase_stop:
            {
                audio_stack_event_usecase_stop_t *event_data = data;
                statsMonitor_UseCaseStopCallback(event_data->common.use_case);
                break;
            }
            default:
            break;
        }
    }
}

static void statsMonitor_UpdateAudioStreaming(void)
{
    streaming_info.updated = FALSE;

    if(streaming_info.active_use_cases)
    {
        streaming_info.updated = ContextFramework_GetContextItem(context_streaming_info, (void *)&streaming_info.ctx_streaming_info, sizeof(context_streaming_info_t)); 
    }
}

static void statsMonitor_UpdateAudioMcps(void)
{
    audio_mcps.updated = FALSE;

    if(audio_mcps.active_use_cases)
    {
        if(OperatorFrameworkGetCoreMcps(AUDIO_CORE_ID_MDSP, &audio_mcps.mdsp_mcps))
        {
            audio_mcps.updated = TRUE;
        }

        if(OperatorFrameworkGetCoreMcps(AUDIO_CORE_ID_LLDSP, &audio_mcps.lldsp_mcps))
        {
            audio_mcps.updated = TRUE;
        }

        if(OperatorFrameworkGetCoreMcps(AUDIO_CORE_ID_ODSP, &audio_mcps.odsp_mcps))
        {
            audio_mcps.updated = TRUE;
        }

        uint16 clk_power_mode_recv;
        if(OperatorFrameworkConfigurationGet(SYSTEM_KEYS_MSG_CLK_POWER_MODE, NULL, 0, &clk_power_mode_recv, 1))
        {
            audio_mcps.updated = TRUE;
            audio_mcps.audio_clock = statsMonitorUtils_ClockIdToMhz(clk_power_mode_recv);
        }
    }
}

static void statsMonitor_LogStats(void)
{
    if(streaming_info.updated)
    {
        context_streaming_info_t* ctx_streaming_info = &streaming_info.ctx_streaming_info;
        STATS_MONITOR_LOG("statsMonitor_LogStats AUDIO STREAMING INFO: is_adaptive %d, is_lossless %d, bitrate %d, rssi %d", 
                                        ctx_streaming_info->common.is_adaptive, ctx_streaming_info->common.is_lossless, ctx_streaming_info->common.bitrate, ctx_streaming_info->common.primary_rssi);
    
        switch(ctx_streaming_info->type)
        {
            case CONTEXT_STREAMING_STATISTICS_TYPE_A2DP:
            {
                audio_a2dp_ttp_toa_delta_histogram_t* ttp_hist = &ctx_streaming_info->u.a2dp.ttp_histogram;
                STATS_MONITOR_LOG(" A2DP: seid %d, link quality %d", ctx_streaming_info->u.a2dp.seid, ctx_streaming_info->u.a2dp.primary_link_quality);
                STATS_MONITOR_LOG(" TTP HISTOGRAM:");
                STATS_MONITOR_LOG("  LATE: %d", ttp_hist->late);
                STATS_MONITOR_LOG("  0-25 ms: %d", ttp_hist->range_0_to_25ms);
                STATS_MONITOR_LOG("  25-50 ms: %d", ttp_hist->range_25_to_50ms);
                STATS_MONITOR_LOG("  50-100 ms: %d", ttp_hist->range_50_to_100ms);
                STATS_MONITOR_LOG("  100-150 ms: %d", ttp_hist->range_100_to_150ms);
                STATS_MONITOR_LOG("  > 150 ms: %d", ttp_hist->beyond_150ms);
                STATS_MONITOR_LOG(" SEQN ERRORS: %d", ctx_streaming_info->u.a2dp.seqn_errors);
            }
            break;

            case CONTEXT_STREAMING_STATISTICS_TYPE_LEA:
            {
                STATS_MONITOR_LOG(" LEA: codec %d", ctx_streaming_info->u.lea.codec_type);
            }
            break;

            default:
            break;
        }
    }

    if(audio_mcps.updated)
    {
        STATS_MONITOR_LOG("statsMonitor_LogStats MCPS");
        STATS_MONITOR_LOG(" MDSP: %d", audio_mcps.mdsp_mcps);
        STATS_MONITOR_LOG(" LLDSP: %d", audio_mcps.lldsp_mcps);
        STATS_MONITOR_LOG(" ODSP: %d", audio_mcps.odsp_mcps);
        STATS_MONITOR_LOG(" AUDIO CLOCK: %d MHz", audio_mcps.audio_clock);
    }
}

static void statsMonitor_HandleReadStats(void)
{
    DEBUG_LOG_V_VERBOSE("statsMonitor_HandleReadStats (every %d ms)", stats_monitor_interval_ms);

    statsMonitor_UpdateAudioStreaming();
    statsMonitor_UpdateAudioMcps();

    statsMonitor_LogStats();

    if(stats_monitor_interval_ms)
    {
        MessageSendLater(STATS_MONITOR_TASK, STATS_MONITOR_INTERNAL_MSG_READ_STATS, NULL, stats_monitor_interval_ms);
    }
}

static void statsMonitor_MsgHandler(Task task, MessageId id, Message msg)
{
    switch(id)
    {
        case STATS_MONITOR_INTERNAL_MSG_READ_STATS:
            statsMonitor_HandleReadStats();
        break;

        default: 
        break;
    }
    
}

void StatsMonitor_Init(void)
{
    event_bus_subscriber_t subscriber = {statsMonitor_OnEvent, NULL};
    EventBus_Subscribe(EVENT_BUS_CHANNEL_AUDIO_STACK, subscriber);

    if(stats_monitor_interval_ms)
    {
        MessageSendLater(STATS_MONITOR_TASK, STATS_MONITOR_INTERNAL_MSG_READ_STATS, NULL, stats_monitor_interval_ms);
    }
}

void StatsMonitor_ChangeInterval(uint16 new_interval)
{
    stats_monitor_interval_ms = new_interval;
    MessageCancelAll(STATS_MONITOR_TASK, STATS_MONITOR_INTERNAL_MSG_READ_STATS);

    if(stats_monitor_interval_ms)
    {
        MessageSendLater(STATS_MONITOR_TASK, STATS_MONITOR_INTERNAL_MSG_READ_STATS, NULL, stats_monitor_interval_ms);
    }   
}

#endif /* INCLUDE_STATS_MONITOR */
